home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / prog / asm_0_m.arj / CUTPASTE.ASM < prev    next >
Assembly Source File  |  1989-06-17  |  39KB  |  1,205 lines

  1.      
  2. ;------------------------------------------------------------------------------
  3. ;           CUTPASTE.ASM
  4. ;           Must be converted to a .COM file
  5. ;------------------------------------------------------------------------------
  6. ;
  7. ;---------------
  8. ;======= Equates
  9. ;---------------
  10. ;
  11. ;------- Window
  12. WNDW_HT     EQU     25
  13. WNDW_WD     EQU     80
  14. ;------- Scan codes -- hot keys
  15. CUT_KEY     EQU     7000H              ; Alt-Fn9
  16. PASTE_KEY   EQU     6600H              ; Ctl-Fn9
  17. ;------- Scan code -- mark key
  18. MARK_CHAR   EQU     3B00H              ; Fn1
  19. ;------- Scan codes -- cursor movement keys
  20. UP_ARROW    EQU     4800H
  21. DOWN_ARROW  EQU     5000H
  22. LEFT_ARROW  EQU     4B00H
  23. RIGHT_ARROW EQU     4D00H
  24. CTL_RGT_ARW EQU     7400H              ; Control-RightArrow
  25. CTL_LFT_ARW EQU     7300H              ; Control-LeftArrow
  26. HOME_KEY    EQU     4700H
  27. END_KEY     EQU     4F00H
  28. TOP_PAGE    EQU     4900H              ; PageUp
  29. BOT_PAGE    EQU     5100H              ; PageDown
  30. ;------- Scan codes -- insert/delete keys
  31. CHAR_INS    EQU     5200H
  32. DELETE      EQU     5300H              ; Delete (lower right)
  33. BACKSPACE   EQU     0008H              ; User rubout (^H) key
  34. LINE_INS    EQU     000DH              ; Enter
  35. DEL_TO_EOL  EQU     4000H              ; Fn6
  36. LINE_DEL    EQU     4100H              ; Fn7
  37. ;------- Scan codes -- carriage return
  38. CAR_RET1    EQU     000DH              ; Carriage return (one byte)
  39. CAR_RET2    EQU     1C0DH              ; Carriage return (two bytes)
  40. ;------- Characters and attributes
  41. BLANK       EQU     0720H              ; Define the blank character
  42. ATTR        EQU     07H                ; Default attribute (white on black)
  43. INV_ATTR    EQU     70H                ; Inverse of ATTR (black on white)
  44. ;
  45. ;
  46. ;======================================= Segment CODESEG
  47. ;
  48. COMSEG      SEGMENT PARA PUBLIC 'CODE'
  49.             ASSUME  CS:COMSEG, DS:COMSEG, ES:COMSEG
  50.             ORG     100H
  51.  
  52. START       LABEL   NEAR
  53.             JMP     INSTALL
  54.  
  55. ;-------------------------
  56. ;======= Data storage area
  57. ;-------------------------
  58. ;
  59. ;------- Copyright information
  60. COPYRITE    DB 'Copyright Gerry Boyd, Larry Weiss, Randy Davis 1985  All Rights Reserved'
  61.             DB '(214)238-9545'
  62. ;------- Buffer to hold screen contents
  63. SCRNSAVE    DW      ( WNDW_HT * (WNDW_WD+1) ) DUP(BLANK)
  64. ;------- Cursor location
  65. CURSOR_POS  DW      0
  66. OLD_CUR_POS DW      0
  67. ;------- Old stack location
  68. SAVSTAKSP   DW      0
  69. SAVSTAKSS   DW      0
  70. ;------- Request type
  71. REQUEST     LABEL   WORD               
  72.  REQUEST0   DB      0
  73.  REQUEST1   DB      0
  74.  REQUEST2   DB      0
  75. ;------- Original keyboard request handler address
  76. OLDINT16    LABEL   DWORD              
  77.  OLDINT16IP DW      0
  78.  OLDINT16CS DW      0
  79. ;------- Define the confines of the window
  80. LEFT_MARG   DB      0                  
  81. RIGHT_MARG  DB      0
  82. TOP_MARG    DB      0
  83. BOT_MARG    DB      0
  84. ;------- Feed chars to application
  85. MARK        DW      -1                 ; Flag used in character feed
  86. FEED_START  DW      0                  ; Address in buffer of begin...
  87. FEED_STOP   DW      0                  ; ...and end of feed
  88. FEED_END1   DW      0                  ; Temp holding spot for beg feed addr
  89. FEED_END2   DW      0                  ; Temp holding spot for end feed addr
  90. FEED_CHAR   DW      0                  ; Char to feed on this call
  91. ;------- Display segment
  92. DISPLAY_SEG DW      0
  93. ;------- Flag decribing movement of data between screen and buffer
  94. SWAP_SAVE   DB      0
  95.  
  96.  
  97. ;-------------------
  98. ;======= Subroutines
  99. ;-------------------
  100.  
  101.             ASSUME  CS:COMSEG, DS:COMSEG, ES:NOTHING
  102.  
  103. ;======= Proc CALC_WINDOW
  104. ;
  105. ;        Calculate window extremeties and store them in 
  106. ;        RIGHT_MARG, LEFT_MARG, TOP_MARG, BOT_MARG
  107. ;
  108. CALC_WINDOW PROC    NEAR
  109. ;------- Find out video mode for calculating window size
  110.             MOV     AH,0FH             
  111.             INT     10H
  112. ;------- Save the video segment
  113.             MOV     CX,0B000H          ; Assume monochrome
  114.             CMP     AL,7               ; Look for mode 7
  115.             JZ      CW100              ; Loop around if mono
  116.             MOV     CX,0B800H          ; No, it's graphics
  117. CW100:      MOV     DISPLAY_SEG,CX
  118. ;------- Save window margins
  119.             DEC     AH                 ; Number of screen columns
  120.             MOV     RIGHT_MARG,AH
  121.             MOV     LEFT_MARG,0
  122.             MOV     TOP_MARG,0
  123.             MOV     BOT_MARG,WNDW_HT-1
  124. ;------- Exit
  125.             RET
  126. CALC_WINDOW ENDP
  127.  
  128.  
  129. ;======= Proc WINDOW_SAVE
  130. WINDOW_SAVE PROC    NEAR
  131.             MOV     SWAP_SAVE,0
  132.             CALL    WIN_SWP_SAV
  133.             RET
  134. WINDOW_SAVE ENDP
  135.  
  136.  
  137. ;======= Proc WINDOW_SWAP
  138. WINDOW_SWAP PROC    NEAR
  139.             MOV     SWAP_SAVE,0FFH
  140.             CALL    WIN_SWP_SAV
  141.             RET
  142. WINDOW_SWAP ENDP
  143.  
  144.  
  145. ;======= Proc WIN_SWP_SAV
  146. ;
  147. ;        When SWAP_SAVE=0, saves screen in buffer SCRNSAVE.
  148. ;        When SWAP_SAVE=FF, exchanges contents of screen SCRNSAVE.
  149. ;        This procedure does no BIOS calls -- all direct writes
  150. ;
  151. WIN_SWP_SAV PROC    NEAR
  152. ;
  153. ;******* Setup for processing loop
  154.             MOV     CX,WNDW_HT         ; Number of rows in window area
  155.             MOV     ES,DISPLAY_SEG     ; Load up the video segment
  156.             MOV     SI,OFFSET SCRNSAVE ; Point SI at beginning of buffer
  157.             XOR     DI,DI              ; DI indexes rows (lines)
  158.             XOR     BX,BX              ; BX indexes columns (chars)
  159. ;
  160. ;******* Loop on lines
  161. WS050       LABEL   NEAR
  162.             MOV     BL,LEFT_MARG       ; Start on this line at left margin
  163. ;------- Process next character and attribute on line
  164. WS100:      SHL     BX,1               ; Change column number to byte pointer
  165.             MOV     AX,ES:[BX][DI]     ; Get the next char/attr from screen
  166.             XCHG    AX,[SI]            ; Store it and write saved char
  167.             ADD     SI,2               ; Move pointer over a word
  168. ;------- Perform swap/save check
  169.             CMP     SWAP_SAVE,0        ; Is this window swap or window save?
  170.             JZ      WS150              ; Loop around if a save
  171.             MOV     ES:[BX][DI],AX     ; For swap, restore that char to screen
  172. ;------- Test for end of line
  173. WS150:      SHR     BX,1               ; Put byte offset back to col number
  174.             INC     BX
  175.             CMP     BL,RIGHT_MARG      ; Are we beyond the end of the line?
  176.             JNA     WS100              ; No, branch and process next char
  177. ;------- Skip down to next line
  178.             MOV     [SI],CAR_RET2      ; So CRs are fed properly later
  179.             ADD     SI,2               ; Move pointer over a word
  180.             SHL     BX,1               ; Adjust DI by one line so that it...
  181.             ADD     DI,BX              ; ...points to beginning of next line
  182.             LOOP    WS050
  183. ;
  184. ;******* Exit
  185.             RET
  186. WIN_SWP_SAV ENDP
  187.  
  188.  
  189. ;======= Proc EDITOR
  190. ;
  191. ;        Check for edit keys (such as Ins, Del, etc).
  192. ;        If not one of those, assume its ASCII and insert it in the screen.
  193. ;        Return when `cut' hotkey detected.
  194. ;
  195. EDITOR      PROC    NEAR
  196. ;
  197. ;******* Get a character and prepare for processing
  198. ED100       LABEL   NEAR
  199. ;------- Read a character from the keyboard
  200.             CALL    GET_CHAR
  201. ;------- If character is ASCII, then null out scan code
  202.             OR      AL,AL            
  203.             JZ      ED105
  204.             XOR     AH,AH
  205. ;------- Remove previous shading (if any)
  206. ED105:      CALL    UNSHAD_SCRN
  207. ;------- Place cursor position in BX (will become new cursor position)
  208.             MOV     BX,CURSOR_POS
  209. ;------- Check for and loop around if not `cut' hotkey pressed
  210.             CMP     AX,CUT_KEY
  211.             JNZ     ED120
  212. ;------- `Cut' hotkey pressed -- set termination conditions
  213.             MOV     MARK,-1
  214.             MOV     FEED_END1,0
  215.             MOV     FEED_END2,0
  216. ;------- Branch to end of routine
  217.             JMP     ED800
  218. ;
  219. ;******* Simple cursor movement keys
  220. ;------- Left arrow
  221. ED120:      CMP     AX,LEFT_ARROW
  222.             JNZ     ED140
  223.             DEC     BL
  224.             JMP     ED500
  225. ;------- Right arrow
  226. ED140:      CMP     AX,RIGHT_ARROW
  227.             JNZ     ED160
  228.             INC     BL
  229.             JMP     ED500
  230. ;------- Up arrow
  231. ED160:      CMP     AX,UP_ARROW
  232.             JNZ     ED180
  233.             DEC     BH
  234.             JMP     ED500
  235. ;------- Down arrow
  236. ED180:      CMP     AX,DOWN_ARROW
  237.             JNZ     ED185
  238.             INC     BH
  239.             JMP     ED500
  240. ;------- Home
  241. ED185:      CMP     AX,HOME_KEY
  242.             JNZ     ED190
  243.             MOV     BL,LEFT_MARG
  244.             JMP     ED500
  245. ;------- End
  246. ED190:      CMP     AX,END_KEY
  247.             JNZ     ED195
  248.             MOV     BL,RIGHT_MARG
  249.             JMP     ED500
  250. ;------- PageUp
  251. ED195:      CMP     AX,TOP_PAGE
  252.             JNZ     ED200
  253.             MOV     BH,TOP_MARG
  254.             JMP     ED500
  255. ;------- PageDown
  256. ED200:      CMP     AX,BOT_PAGE
  257.             JNZ     ED205
  258.             MOV     BH,BOT_MARG
  259.             JMP     ED500
  260. ;
  261. ;******* Control-RightArrow and Control-LeftArrow
  262. ;------- Check for Control-RightArrow and set increment
  263. ED205:      CMP     AX,CTL_RGT_ARW
  264.             JNZ     ED210
  265.             MOV     CL,1               ; Set increment to go forward
  266.             JMP     ED213
  267. ;------- Check for Control-LeftArrow and set increment
  268. ED210:      CMP     AX,CTL_LFT_ARW
  269.             JNZ     ED220
  270.             CMP     BL,LEFT_MARG
  271.             JLE     ED219
  272.             MOV     CL,-1              ; Set increment to go backward
  273.             DEC     BL                 ; Begin one char to left
  274. ;------- Get and save character at current position
  275. ED213:      MOV     DX,BX              ; Use DX for cursor for READ_CHAR
  276.             CALL    READ_CHAR          ; Get char at current position
  277.             MOV     CH,AL              ; Save current char in CH
  278. ;------- Move one character location and check if margin violated
  279. ED215:      ADD     DL,CL              ; Move over one character
  280.             CMP     DL,RIGHT_MARG      ; Stop at left/right margins
  281.             JGE     ED218
  282.             CMP     DL,LEFT_MARG
  283.             JLE     ED218
  284. ;------- Test for change from space to non-space
  285.             CALL    READ_CHAR          ; Read current character
  286.             CMP     CH,' '             ; Branch if original...
  287.             JNZ     ED216              ; ...was not a space
  288.             CMP     AL,' '             ; Is this a space?
  289.             JZ      ED215              ; Yes, branch and repeat
  290.             JMP     ED217              ; No, stop
  291. ;------- Test for change from non-space to space
  292. ED216:      CMP     AL,' '             ; Is this a space?
  293.             JNZ     ED215              ; No, branch and repeat
  294. ;------- If we were going towards left then move to the right by one char
  295. ED217:      CMP     CL,-1
  296.             JNZ     ED218
  297.             ADD     DL,1               
  298. ;------- Place new cursor position in BX
  299. ED218:      MOV     BX,DX
  300. ;------- Branch to end of key processing loop
  301. ED219:      JMP     ED500
  302. ;
  303. ;******* Erase remainder of line
  304. ;------- Test for, and go to next case if not, DEL_TO_EOL key
  305. ED220:      CMP     AX,DEL_TO_EOL
  306.             JNZ     ED240
  307. ;------- Use ERASE_LINE proc to erase rest of line
  308.             MOV     DX,BX
  309.             CALL    ERASE_LINE
  310. ;------- Branch to end of key processing loop
  311.             JMP     ED500
  312. ;
  313. ;******* Backspace
  314. ;------- Test for, and go to next case if not, BACKSPACE key
  315. ED240:      CMP     AX,BACKSPACE
  316.             JNZ     ED260
  317. ;------- Move to left one space
  318.             DEC     BL
  319. ;------- Test and branch if we didn't move off screen
  320.             CMP     BL,LEFT_MARG
  321.             JGE     ED250
  322. ;------- Put cursor at left margin and go to bottom of processing loop
  323.             MOV     BL,LEFT_MARG
  324.             JMP     ED500
  325. ;------- Jump to Delete key processing to the actual delete
  326. ED250:      JMP     ED270
  327. ;
  328. ;******* Delete key
  329. ED260       LABEL   NEAR
  330. ;------- Test for and branch if not Del key
  331.             CMP     AX,DELETE
  332.             JNZ     ED280
  333. ;------- Set CX to number of spaces to right of current position
  334. ED270:      MOV     CL,RIGHT_MARG
  335.             SUB     CL,BL
  336.             XOR     CH,CH
  337. ;------- Set DX to current cursor position
  338.             MOV     DX,BX
  339. ;------- Branch if CX is zero
  340.             JCXZ    ED275
  341. ;------- Loop which moves CX characters one space to left
  342. ED272:      INC     DL
  343.             CALL    READ_CHAR
  344.             DEC     DL
  345.             CALL    WRITE_CHAR
  346.             INC     DL
  347.             LOOP    ED272
  348. ;------- Place BLANK at current cursor position
  349. ED275:      MOV     AX,BLANK
  350.             CALL    WRITE_CHAR
  351. ;------- Branch to end of key processing loop
  352.             JMP     ED500
  353. ;
  354. ;******* Insert key
  355. ED280       LABEL   NEAR
  356. ;------- Test for, and go to next case if not, Insert key
  357.             CMP     AX,CHAR_INS
  358.             JNZ     ED300
  359. ;------- Set DX to right margin of current row
  360.             MOV     DH,BH
  361.             MOV     DL,RIGHT_MARG
  362. ;------- Set CX to number of spaces to right of current position
  363.             MOV     CL,DL              
  364.             SUB     CL,BL
  365.             XOR     CH,CH
  366. ;------- Branch if CX is zero
  367.             JCXZ    ED290
  368. ;------- Loop which moves CX characters one space to right
  369. ED285:      DEC     DL
  370.             CALL    READ_CHAR
  371.             INC     DL
  372.             CALL    WRITE_CHAR
  373.             DEC     DL
  374.             LOOP    ED285
  375. ;------- Place BLANK at current cursor position
  376. ED290:      MOV     AX,BLANK
  377.             CALL    WRITE_CHAR
  378. ;------- Branch to end of key processing loop
  379.             JMP     ED500
  380. ;
  381. ;******* LINE_INS key
  382. ED300       LABEL   NEAR
  383. ;------- Test for, and go to next case if not, LINE_INS key
  384.             CMP     AX,LINE_INS
  385.             JNZ     ED320
  386. ;------- Start at the bottom margin
  387.             MOV     DH,BOT_MARG
  388. ;------- Start at the left margin
  389. ED305:      MOV     DL,LEFT_MARG
  390. ;------- Test for and branch if we're on the current line
  391.             CMP     DH,BH
  392.             JZ      ED315
  393. ;------- Assign loop counter
  394.             XOR     CX,CX
  395.             MOV     CL,RIGHT_MARG
  396.             INC     CX
  397. ;------- Loop to move row of char/attr down
  398. ED310:      DEC     DH
  399.             CALL    READ_CHAR          ; Get the character
  400.             INC     DH
  401.             CALL    WRITE_CHAR         ; And put it back one line higher
  402.             INC     DL                 ; Move right one character
  403.             LOOP    ED310
  404. ;------- Now move up a line and do it again
  405.             DEC     DH                 
  406.             JMP     ED305
  407. ;------- Erase the current line
  408. ED315:      CALL    ERASE_LINE
  409. ;------- Branch to end of key processing loop
  410.             JMP     ED500
  411. ;
  412. ;******* LINE_DEL key
  413. ED320       LABEL   NEAR
  414. ;------- Test for, and go to next case if not, LINE_DEL key
  415.             CMP     AX,LINE_DEL
  416.             JNZ     ED340
  417. ;------- Start at the left margin
  418. ED325:      MOV     DL,LEFT_MARG
  419. ;------- Test for and branch if we're on the last line
  420.             CMP     DH,BOT_MARG
  421.             JZ      ED335
  422. ;------- Assign loop counter
  423.             XOR     CX,CX
  424.             MOV     CL,RIGHT_MARG
  425.             INC     CX
  426. ;------- Loop to move row of char/attr up
  427. ED330:      INC     DH
  428.             CALL    READ_CHAR          ; Get the character
  429.             DEC     DH
  430.             CALL    WRITE_CHAR         ; And put it back one line lower
  431.             INC     DL                 ; Move right one character
  432.             LOOP    ED330
  433. ;------- Now move down a line and do it again
  434.             INC     DH
  435.             JMP     ED325
  436. ;------- Erase bottom line on display
  437. ED335:      CALL    ERASE_LINE         ; And wipe out the bottom line
  438. ;------- Branch to end of key processing loop
  439.             JMP     ED500
  440. ;
  441. ;******* First MARK_CHAR
  442. ED340       LABEL   NEAR
  443. ;------- Test for, and go to next case if not, `mark' hotkey
  444.             CMP     AX,MARK_CHAR
  445.             JNZ     ED400
  446. ;------- Put cursor position in DX
  447.             MOV     DX,BX
  448. ;------- Test for and loop around if already marking
  449.             CMP     MARK,-1
  450.             JNZ     ED350
  451. ;------- Save cursor location in MARK
  452.             MOV     MARK,BX            
  453. ;------- Branch to end of key processing loop
  454.             JMP     ED500
  455. ;
  456. ;******* Second MARK_CHAR
  457. ;------- Store current location as FEED_END1 address in SCRNSAVE buffer
  458. ED350:      CALL    CONVERT_LOC
  459.             MOV     FEED_END1,AX
  460. ;------- Store previous mark location as feed end address SCRNSAVE buffer
  461.             MOV     DX,MARK
  462.             CALL    CONVERT_LOC
  463.             MOV     FEED_END2,AX
  464. ;------- Ensure that FEED_END1 is less than or equal to FEED_END2
  465.             MOV     CX,FEED_END1
  466.             CMP     CX,AX
  467.             JNA     ED360
  468.             MOV     FEED_END1,AX
  469.             MOV     FEED_END2,CX
  470. ;------- Increase FEED_END2 by one cell
  471. ED360:      MOV     AX,FEED_END2
  472.             ADD     AX,2
  473.             MOV     FEED_END2,AX
  474. ;------- Trim unmarked chars from screen
  475.             CALL    TRIM_SCREEN
  476. ;------- Branch to exit routine
  477.             JMP     ED800
  478. ;
  479. ;******* ASCII char -- write at current position
  480. ED400:      MOV     AH,ATTR
  481.             CALL    W_CHAR
  482.             INC     BL                 ; Move over one position
  483. ;
  484. ;******* Adjust cursor position
  485. ;------- Right margin
  486. ED500:      CMP     BL,RIGHT_MARG
  487.             JLE     ED520
  488.             MOV     BL,RIGHT_MARG
  489. ;------- Left margin
  490. ED520:      CMP     BL,LEFT_MARG
  491.             JGE     ED540
  492.             MOV     BL,LEFT_MARG
  493. ;------- Top margin
  494. ED540:      CMP     BH,TOP_MARG
  495.             JGE     ED560
  496.             MOV     BH,TOP_MARG
  497. ;------- Bottom margin
  498. ED560:      CMP     BH,BOT_MARG
  499.             JLE     ED600
  500.             MOV     BH,BOT_MARG
  501. ;------- Save new cursor position in CURSOR_POS
  502. ED600:      MOV     CURSOR_POS,BX
  503. ;------- Move cursor to the new position
  504.             MOV     DX,BX
  505.             CALL    PUT_CURSOR
  506. ;------- Jump to beginning of processing loop
  507.             JMP     ED100
  508. ;
  509. ;******* End routine
  510. ED800:      RET
  511. ;
  512. EDITOR      ENDP
  513.  
  514.  
  515. ;======= Proc PUT_CURSOR
  516. ;
  517. ;        Place cursor at location in DX
  518. ;
  519. PUT_CURSOR  PROC    NEAR
  520. ;------- If we are in mark mode, set the screen square to inverse video
  521.             CALL    SHADE_SCRN
  522.             MOV     AH,02H             ; SET_CUR_POS service
  523.             PUSH    BX
  524.             XOR     BX,BX              ; Page 0
  525.             INT     10H
  526.             POP     BX
  527.             RET
  528. PUT_CURSOR  ENDP
  529.  
  530. UP_LEFT     LABEL   WORD
  531.  ULHC_C     DB      0
  532.  ULHC_R     DB      0
  533. LW_RIGHT    LABEL   WORD
  534.  LRHC_C     DB      0
  535.  LRHC_R     DB      0
  536. ATTRIB      DB      0
  537. SAVE_AX     DW      0
  538. SAVE_DX     DW      0
  539.  
  540. ;======= Proc SHADE_SCRN
  541. ;
  542. ;        Assigns INV_ATTR to characters in box defined by
  543. ;        MARK and CURSOR_POS.  Called by PUT_CURSOR.
  544. ;
  545. SHADE_SCRN  PROC    NEAR
  546. ;
  547. ;******* Initialization
  548. ;------- Test for, and branch to routine end, if not marking
  549.             CMP     MARK,-1            ; Is a mark set?
  550.             JZ      SS450
  551. ;------- Save AX and DX registers
  552.             MOV     SAVE_AX,AX
  553.             MOV     SAVE_DX,DX
  554. ;------- First set the attribute to inverse
  555.             MOV     ATTRIB,INV_ATTR    
  556. ;
  557. ;******* Place upper left-hand corner of shaded box in UP_LEFT
  558. ;------- Place current position in BX and marked position in AX
  559.             MOV     BX,CURSOR_POS
  560.             MOV     AX,MARK            
  561. ;------- Place upper left-hand corner column value in AL
  562.             CMP     BL,AL
  563.             JA      SS100
  564.             MOV     AL,BL
  565. ;------- Place upper left-hand corner row value in AH
  566. SS100:      CMP     BH,AH
  567.             JA      SS200
  568.             MOV     AH,BH
  569. ;------- Store upper left-hand corner in UP_LEFT
  570. SS200:      MOV     UP_LEFT,AX
  571. ;
  572. ;******* Place lower right-hand corner of shaded box in LW_RIGHT
  573. ;------- Place marked position in AX
  574.             MOV     AX,MARK
  575. ;------- Place column value in AL
  576.             CMP     BL,AL
  577.             JBE     SS300
  578.             MOV     AL,BL
  579. ;------- Place row value in AH
  580. SS300:      CMP     BH,AH
  581.             JBE     SS400
  582.             MOV     AH,BH
  583. ;------- Store LRH corner in LW_RIGHT
  584. SS400:      MOV     LW_RIGHT,AX
  585. ;
  586. ;******* Call SS_COMMON to do the shading
  587.             CALL    SS_COMMON
  588. ;
  589. ;******* Return to caller
  590. SS450:      RET
  591. SHADE_SCRN  ENDP
  592.  
  593.  
  594. ;======= Proc UNSHAD_SCRN
  595. ;
  596. ;        Assign ATTR to attributes of characters in the box defined
  597. ;        by UP_LEFT and LW_RIGHT.  Called by EDITOR, at the beginning
  598. ;        of key processing.
  599. ;
  600. UNSHAD_SCRN PROC    NEAR
  601. ;
  602. ;------- Test for, and branch to routine end, if not marking
  603.             CMP     MARK,-1            ; Here we unshade the shaded area
  604.             JZ      SS475
  605. ;------- Save AX and DX registers
  606.             MOV     SAVE_AX,AX
  607.             MOV     SAVE_DX,DX
  608. ;------- Set the attribute to normal
  609.             MOV     ATTRIB,ATTR        ; Change the box back to normal
  610. ;------- Call SS_COMMON to do the shading
  611.             CALL    SS_COMMON
  612. ;------- Return to caller
  613. SS475:      RET
  614. ;
  615. UNSHAD_SCRN ENDP
  616.  
  617.  
  618. ;======= Proc SS_COMMON
  619. ;
  620. ;        Assign ATTRIB to attributes of characters in the box defined
  621. ;        by UP_LEFT and LW_RIGHT.
  622. ;
  623. SS_COMMON   PROC    NEAR
  624. ;
  625. ;******* Initialization
  626. ;------- Put box corners in BX and DX registers
  627.             MOV     DX,UP_LEFT         ; DX is variable -- loc to change attr
  628.             MOV     BX,LW_RIGHT        ; BX is constant
  629. ;
  630. ;******* Change attributes of row in DH
  631. ;------- Test and branch if past last column
  632. SS500:      CMP     DL,BL
  633.             JA      SS600
  634. ;------- Change the attribute
  635.             MOV     AH,ATTRIB
  636.             CALL    WRITE_ATTR
  637. ;------- Point to next char on row and branch to top
  638.             INC     DL
  639.             JMP     SS500
  640. ;
  641. ;******* Prepare to do next row
  642. ;------- Point to next row
  643. SS600:      INC     DH
  644. ;------- Branch if past last row
  645.             CMP     DH,BH
  646.             JA      SS700
  647. ;------- Put first column in DL and branch
  648.             MOV     DL,ULHC_C
  649.             JMP     SS500
  650. ;
  651. ;******* End program
  652. ;------- Restore AX and DX
  653. SS700:      MOV     AX,SAVE_AX
  654.             MOV     DX,SAVE_DX
  655. ;------- Return
  656.             RET
  657. ;
  658. SS_COMMON   ENDP
  659.  
  660.  
  661. ;======= Proc TRIM_SCREEN
  662. ;
  663. ;        Trims the right margin of screen after the second mark
  664. ;
  665. TRIM_SCREEN PROC    NEAR
  666. ;
  667. ;******* Initialization
  668. ;------- First clear mark
  669.             MOV     MARK,-1
  670. ;------- Test and branch to return if at right-hand edge of screen
  671.             MOV     DL,LRHC_C
  672.             CMP     DL,RIGHT_MARG
  673.             JZ      TS200
  674. ;------- Put space to right of upper right-hand corner in BX
  675.             MOV     BH,ULHC_R
  676.             MOV     BL,LRHC_C
  677.             INC     BL
  678.  
  679. ;******* Loop to erase to right of box
  680. ;------- Place lower right-hand corner in DX
  681. TS100:      MOV     DX,LW_RIGHT
  682. ;------- Branch to end if BH equals DH
  683.             CMP     BH,DH
  684.             JZ      TS200
  685. ;------- Erase end of current line
  686.             MOV     DX,BX
  687.             CALL    ERASE_LINE
  688. ;------- Increment current line and repeat
  689.             INC     BH
  690.             JMP     TS100
  691. ;
  692. ;******* End routine
  693. TS200:      RET
  694. TRIM_SCREEN ENDP
  695.  
  696.  
  697. ;======= Proc TRIM_BUF
  698. ;
  699. ;        Trims blanks from first and last row in the feed area
  700. ;
  701. TRIM_BUF    PROC    NEAR
  702. ;
  703. ;******* Exit unless buffer holds data
  704.             MOV     BX,FEED_END2
  705.             CMP     BX,FEED_END1
  706.             JZ      TB070
  707. ;
  708. ;******* Trim trailing blanks off last row
  709. ;------- Set CX to number of chars in a row
  710.             XOR     CX,CX
  711.             MOV     CL,LRHC_C
  712.             SUB     CL,ULHC_C
  713.             INC     CX
  714. ;------- Move backwards until first non-blank is found
  715. TB010:      MOV     AX,[BX-2]
  716.             CMP     AL,' '
  717.             JNZ     TB020
  718.             SUB     BX,2
  719.             LOOP    TB010
  720. ;------- Reassign FEED_END2
  721. TB020:      MOV     FEED_END2,BX
  722. ;
  723. ;******* Check for, and skip over, a blank first line
  724. ;------- Branch and end if feed area corresponds to one row
  725.             MOV     AX,UP_LEFT
  726.             CMP     AH,LRHC_R
  727.             JZ      TB070
  728. ;------- Set CX to number of chars in a row
  729.             XOR     CX,CX
  730.             MOV     CL,LRHC_C
  731.             SUB     CL,ULHC_C
  732.             INC     CX
  733. ;------- Point BX to start of feed area
  734.             MOV     BX,FEED_END1
  735. ;------- Check for, and branch to end, if a non-blank char is found
  736. TB030:      MOV     AX,[BX]
  737.             CMP     AL,' '
  738.             JNZ     TB070
  739.             ADD     BX,2
  740.             LOOP    TB030
  741. ;------- Set AX to number of chars from left side to carriage return
  742.             XOR     AX,AX
  743.             MOV     AL,RIGHT_MARG
  744.             INC     AL
  745.             SUB     AL,ULHC_C
  746. ;------- Convert to words and add to FEED_END1
  747.             SHL     AX,1
  748.             ADD     FEED_END1,AX
  749.  
  750. ;******* End routine
  751. TB070:      RET
  752. ;
  753. TRIM_BUF ENDP
  754.  
  755.  
  756. ;======= Proc READ_CURSOR
  757. ;
  758. ;        Find the current cursor location and place in DX
  759. ;
  760. READ_CURSOR PROC    NEAR               
  761.             MOV     AH,03H
  762.             PUSH    BX
  763.             XOR     BX,BX
  764.             INT     10H
  765.             POP     BX
  766.             RET
  767. READ_CURSOR ENDP
  768.  
  769.  
  770. ;======= Proc READ_CHAR
  771. ;
  772. ;        Read character on screen at location in DX and place in AX
  773. ;
  774. READ_CHAR   PROC    NEAR
  775. ;------- Covert the row and column into location
  776.             CALL    CAL_VID_LOC
  777. ;------- Get the character and attribute at that location
  778.             MOV     AX,ES:[DI]         
  779.             RET
  780. READ_CHAR   ENDP
  781.  
  782.  
  783. ;======= Proc R_CHAR
  784. ;
  785. ;        Read character on screen at current cursor location and place in AX
  786. ;
  787. R_CHAR      PROC    NEAR
  788.             MOV     AH,08H
  789.             PUSH    BX
  790.             XOR     BH,BH
  791.             INT     10H
  792.             POP     BX
  793.             RET
  794. R_CHAR      ENDP
  795.  
  796. ;======= Proc WRITE_CHAR
  797. ;
  798. ;        Write character and attribute in AX to screen at location in DX
  799. ;
  800. WRITE_CHAR  PROC    NEAR
  801.             PUSH    AX                 ; Save the attrib from destruction
  802.             CALL    CAL_VID_LOC        ; Convert the row and col into location
  803.             POP     AX
  804.             MOV     ES:[DI],AX
  805.             RET
  806. WRITE_CHAR  ENDP
  807.  
  808.  
  809. ;======= Proc WRITE_ATTR
  810. ;
  811. ;        Write attrib in AX to screen location in DX
  812. ;
  813. WRITE_ATTR  PROC    NEAR
  814.             PUSH    AX
  815.             CALL    CAL_VID_LOC
  816.             POP     AX
  817.             MOV     ES:[DI+1],AH
  818.             RET
  819. WRITE_ATTR  ENDP
  820.  
  821.  
  822. ;======= Proc W_CHAR
  823. ;
  824. ;        Write character and attribute in AX at current cursor location
  825. ;
  826. W_CHAR      PROC    NEAR
  827. ;------- Save registers
  828.             PUSH    BX
  829.             PUSH    CX
  830. ;------- Do the write using INT 10H
  831.             MOV     BL,AH              ; Place attribute in BL
  832.             XOR     BH,BH              ; Page 0
  833.             MOV     CX,1               ; Count = 1
  834.             MOV     AH,09H             ; WRITE_CHAR_ATTR service
  835.             INT     10H
  836. ;------- Restore registers
  837.             POP     CX
  838.             POP     BX
  839. ;------- Exit
  840.             RET
  841. W_CHAR      ENDP
  842.  
  843. ;======= Proc GET_CHAR
  844. ;
  845. ;        Get character from keyboard into AX
  846. ;
  847. GET_CHAR    PROC    NEAR
  848.             MOV     AH,0
  849.             PUSHF
  850.             CALL    OLDINT16
  851.             RET
  852. GET_CHAR    ENDP
  853.  
  854. ;======= Proc ERASE_LINE
  855. ;
  856. ;        Erase the current line on the screen from DX to RIGHT_MARG, inclusive
  857. ;
  858. ERASE_LINE  PROC    NEAR
  859. ;------- Set CX to number of columns to erase
  860.             XOR     CX,CX
  861.             MOV     CL,RIGHT_MARG
  862.             SUB     CL,DL
  863.             INC     CL
  864. ;------- Loop to blank row
  865. ER100:      MOV     AX,BLANK
  866.             CALL    WRITE_CHAR
  867.             INC     DL
  868.             LOOP    ER100
  869. ;------- Exit routine
  870.             RET
  871. ERASE_LINE  ENDP
  872.  
  873.  
  874. ;======= Proc CONVERT_LOC
  875. ;
  876. ;        Convert a location on the screen (in DX) into an offset in 
  877. ;        the SCRNSAVE buffer (in AX).
  878. ;
  879. CONVERT_LOC PROC    NEAR
  880. ;
  881. ;******* Initialize
  882. ;------- Place line length (in bytes) in BX
  883.             XOR     BX,BX
  884.             MOV     BL,RIGHT_MARG
  885.             INC     BX
  886.             INC     BX
  887.             SHL     BX,1
  888. ;------- Place location in CX
  889.             MOV     CX,DX
  890. ;------- Point AX at beginning of SCRNSAVE buffer
  891.             MOV     AX,OFFSET SCRNSAVE
  892. ;
  893. ;******* Loop to find row
  894. ;------- Test and branch if CH equals top row of window
  895. CL100:      CMP     CH,TOP_MARG
  896.             JZ      CL200
  897. ;------- Add a row to AX and repeat test
  898.             ADD     AX,BX
  899.             DEC     CH
  900.             JMP     CL100
  901. ;
  902. ;******* Loop to find column
  903. ;------- Test and branch if CL equals left margin of window
  904. CL200:      CMP     CL,LEFT_MARG
  905.             JZ      CL300
  906. ;------- Add a column and repeat
  907.             ADD     AX,2
  908.             DEC     CL
  909.             JMP     CL200
  910. ;
  911. ;******* Exit
  912. CL300:      RET
  913. CONVERT_LOC ENDP
  914.  
  915.  
  916. ;======= Proc CAL_VID_LOC
  917. ;
  918. ;        Convert the screen location (row and column) in DX into an
  919. ;        offset into the video display buffer in DI
  920. ;
  921. CAL_VID_LOC PROC    NEAR
  922. ;------- Initialize AX, CX, DI to zero
  923.             PUSH    CX                 ; Save CX -- some callers need it
  924.             XOR     AX,AX              ; AX holds line length
  925.             XOR     CX,CX              ; CX is temp for location
  926.             XOR     DI,DI              ; DI holds offset
  927. ;------- Set AX to line length in chars
  928.             MOV     AL,RIGHT_MARG
  929.             INC     AX
  930. ;------- Set CX to row number and branch if zero
  931.             MOV     CL,DH              ; Put the number of rows into cx
  932.             JCXZ    CVL200
  933. ;------- Increment DI by the line length times the row number
  934. CVL100:     ADD     DI,AX
  935.             LOOP    CVL100
  936. ;------- Increment DI by the column number
  937. CVL200:     MOV     AL,DL
  938.             ADD     DI,AX
  939. ;------- End routine
  940.             POP     CX                 ; Restore CX
  941.             SHL     DI,1               ; Now convert this into byte offset
  942.             MOV     ES,DISPLAY_SEG
  943.             RET
  944. CAL_VID_LOC ENDP
  945.  
  946.  
  947. ;-------------------------------
  948. ;======= Replacement for INT 16H
  949. ;-------------------------------
  950. ;
  951.             ASSUME  CS:COMSEG, DS:NOTHING, ES:NOTHING
  952. BEGIN       PROC    FAR
  953. ;
  954. ;======= Initial processing -- figure out what to do
  955. ;
  956. ;******* Check if in middle of feeding characters
  957. MAINLOOP    LABEL   NEAR
  958. ;------- Save caller's AX register
  959.             MOV     REQUEST,AX
  960.             AND     AH,0EFH
  961.             MOV     REQUEST2,AH
  962. ;------- Branch if we're in the middle of feeding characters
  963.             MOV     AX,FEED_START      ; Are we in the middle of feeding...
  964.             CMP     AX,FEED_STOP       ; ...chars to the application?
  965.             JZ      NOFEED             ; No, loop around
  966.             JMP     FEED               ; Yes, branch
  967. ;
  968. ;******* Test for type-0 request and return if not
  969. NOFEED      LABEL   NEAR
  970. ;------- Restore caller's AX
  971.             MOV     AX,REQUEST         
  972. ;------- Check for type-0 request -- only interested in char requests
  973.             CMP     REQUEST2,0
  974.             JZ      CONTINU
  975. ;------- Jump to old INT 16 routine, which will return to application
  976.             JMP     OLDINT16
  977. ;
  978. ;******* Now check for a hot key -- return if not
  979. CONTINU     LABEL   NEAR
  980. ;------- Call old INT 16H (emulate interrupt)
  981.             PUSHF
  982.             CALL    OLDINT16
  983. ;------- Check for Alt-Fn9
  984.             CMP     AX,CUT_KEY
  985.             JZ      GOT_CUT_KEY
  986. ;------- Check for Ctl-Fn9
  987.             CMP     AX,PASTE_KEY
  988.             JZ      PASTE_CHAR
  989. ;------- Not a hotkey -- provide keystroke to caller
  990.             IRET
  991. ;
  992. ;======= `Cut' hotkey processing
  993. ;
  994. ;******* Setup for subroutine calls
  995. GOT_CUT_KEY LABEL   NEAR
  996. ;------- Save stack and point SS:SP to top of PSP
  997.             MOV     SAVSTAKSS,SS
  998.             MOV     SAVSTAKSP,SP
  999.             MOV     AX,CS
  1000.             MOV     SS,AX
  1001.             MOV     SP,100H
  1002. ;------- Enable interrupts while processing characters
  1003.             STI
  1004. ;------- Set up a stack frame
  1005.             PUSH    BP
  1006.             MOV     BP,SP
  1007. ;------- Save registers
  1008.             SUB     SP,0EH
  1009.             CALL    SAVEREG
  1010. ;------- Set DS to COMSEG
  1011.             MOV     DS,AX
  1012.             ASSUME  DS:COMSEG
  1013. ;
  1014. ;******* Call EDITOR and other subroutines
  1015. ;------- Calculate window extremeties
  1016.             CALL    CALC_WINDOW
  1017. ;------- Save the cursor for later restoring
  1018.             CALL    READ_CURSOR
  1019.             MOV     OLD_CUR_POS,DX
  1020. ;------- Save screen in buffer SCRNSAVE
  1021.             CALL    WINDOW_SAVE
  1022. ;------- Restore cursor in edit window
  1023.             MOV     DX,CURSOR_POS
  1024.             CALL    PUT_CURSOR
  1025. ;------- Edit in the window
  1026.             CALL    EDITOR
  1027. ;------- Save cursor in edit window for next edit
  1028.             CALL    READ_CURSOR
  1029.             MOV     CURSOR_POS,DX      
  1030. ;------- Put back whatever was originally there
  1031.             CALL    WINDOW_SWAP        
  1032. ;------- Trim blanks from end of buffer
  1033.             CALL    TRIM_BUF
  1034. ;------- Restore the cursor position
  1035.             MOV     DX,OLD_CUR_POS     
  1036.             CALL    PUT_CURSOR
  1037. ;
  1038. ;******* End `cut-key' processing
  1039. ;------- Restore registers
  1040.             CALL    RESTREG
  1041.             ADD     SP,0EH
  1042.             ASSUME  DS:NOTHING
  1043. ;------- Restore stack
  1044.             POP     BP
  1045.             MOV     SS,SAVSTAKSS
  1046.             MOV     SP,SAVSTAKSP
  1047. ;------- End of loop -- jump to beginning
  1048.             MOV     AX,REQUEST         ; Restore request
  1049.             JMP     MAINLOOP           ; Get another char to return to caller
  1050. ;
  1051. ;======= `Paste' hotkey processing
  1052. ;
  1053. ;******* Paste characters into application
  1054. PASTE_CHAR  LABEL   NEAR
  1055.             MOV     AX,FEED_END1
  1056.             MOV     FEED_START,AX
  1057.             MOV     AX,FEED_END2
  1058.             MOV     FEED_STOP,AX
  1059.             MOV     AX,REQUEST         ; Restore request
  1060.             JMP     MAINLOOP           ; Go get another character to return
  1061. ;
  1062. ;******* Feed characters to application from the buffer SCRNSAVE
  1063. FEED        LABEL   NEAR
  1064. ;------- Test and branch for return if a type-2 request
  1065.             CMP     REQUEST2,1
  1066.             JA      KSTAT
  1067. ;------- Get next char from SCRNSAVE buffer into AX
  1068.             PUSH    BX
  1069.             MOV     BX,AX
  1070.             MOV     AX,CS:[BX]
  1071.             POP     BX
  1072. ;------- Loop around if end of screen line
  1073.             CMP     AX,CAR_RET2
  1074.             JZ      FEED10
  1075. ;------- Null attribute
  1076.             XOR     AH,AH
  1077. ;------- Save in FEED_CHAR
  1078. FEED10:     MOV     FEED_CHAR,AX
  1079. ;------- Test and branch if type-1 request
  1080.             CMP     REQUEST2,0
  1081.             JNZ     FEED_STAT
  1082. ;------- Point to next character
  1083.             ADD     FEED_START,2
  1084. ;
  1085. ;******* Handle beginning and end of line
  1086. ;------- Test for and loop around if not carriage return
  1087.             CMP     AL,CAR_RET1
  1088.             JNZ     FEED20
  1089. ;------- Skip to beginning of the feed area
  1090.             PUSH    DX
  1091.             XOR     DX,DX
  1092.             MOV     DL,ULHC_C
  1093.             SHL     DX,1               ; In words
  1094.             ADD     FEED_START,DX
  1095.             POP     DX
  1096. ;------- Put address of next buffer location in BX
  1097. FEED20:     PUSH    BX
  1098.             MOV     BX,FEED_START
  1099. ;------- Test, and branch to return, if at end of feed area
  1100. FEED30:     CMP     BX,FEED_STOP
  1101.             JZ      FEED50
  1102. ;------- Test and loop around if next char is space
  1103.             MOV     AX,CS:[BX]
  1104.             CMP     AL,' '
  1105.             JZ      FEED40
  1106. ;------- Test and branch if a non-space char follows on this line
  1107.             CMP     AX,CAR_RET2
  1108.             JNZ     FEED50
  1109. ;------- Point to carriage return, skipping over trailing blanks
  1110.             MOV     FEED_START,BX
  1111.             JMP     FEED50
  1112. ;------- Point to next char on line and branch to top of loop
  1113. FEED40:     INC     BX
  1114.             INC     BX
  1115.             JMP     FEED30
  1116. ;
  1117. ;******* Exit to caller
  1118. ;------- Exit for type-0 request -- returning character to application
  1119. FEED50      LABEL   NEAR
  1120.             POP     BX                 ; Restore stack
  1121.             MOV     AX,FEED_CHAR       ; Place char to feed in AX
  1122.             IRET                       ; Return char to application
  1123. ;------- Exit for type-1 request: enable interrupts, feed ZF = NZ and char
  1124. FEED_STAT   LABEL   NEAR
  1125.             STI                        
  1126.             RET     02
  1127. ;------- Exit for type-2 request -- invoke INT 16H to do what he wants
  1128. KSTAT:      MOV     AX,REQUEST
  1129.             JMP     OLDINT16
  1130.  
  1131. BEGIN       ENDP
  1132.  
  1133.  
  1134. ;======= Proc SAVEREG
  1135. ;------- Save registers on stack frame
  1136. SAVEREG     PROC    NEAR
  1137.             MOV     [BP-2],BX
  1138.             MOV     [BP-4],CX
  1139.             MOV     [BP-6],DX
  1140.             MOV     [BP-8],SI
  1141.             MOV     [BP-0AH],DI
  1142.             MOV     [BP-0CH],DS
  1143.             MOV     [BP-0EH],ES
  1144.             RET
  1145. SAVEREG     ENDP
  1146.  
  1147. ;======= Proc RESTREG
  1148. ;------- Now put the registers back
  1149. RESTREG     PROC    NEAR
  1150.             MOV     BX,[BP-2]
  1151.             MOV     CX,[BP-4]
  1152.             MOV     DX,[BP-6]
  1153.             MOV     SI,[BP-8]
  1154.             MOV     DI,[BP-0AH]
  1155.             MOV     DS,[BP-0CH]
  1156.             MOV     ES,[BP-0EH]
  1157.             RET
  1158. RESTREG     ENDP
  1159.  
  1160. ;------- Upper limit resident program (paras)
  1161. RESPARA     EQU     ($-START+100H+15)/16
  1162.  
  1163.  
  1164. ;-------------------------
  1165. ;======= Installation code
  1166. ;-------------------------
  1167.  
  1168. INSTALL     LABEL   NEAR
  1169.             ASSUME  CS:COMSEG, DS:COMSEG, ES:COMSEG
  1170.  
  1171. ;------- Output 'OK' message
  1172.             MOV     DX,OFFSET MESSAGE
  1173.             MOV     AH,09H
  1174.             INT     21H
  1175. ;------- Get existing 16H vector using ES:BX
  1176.             MOV     AX,3516H
  1177.             INT     21H
  1178.             MOV     OLDINT16IP,BX
  1179.             MOV     OLDINT16CS,ES
  1180.             ASSUME  ES:NOTHING
  1181. ;------- Now put our routine there using DS:DX
  1182.             MOV     AX,2516H
  1183.             MOV     DX,OFFSET BEGIN
  1184.             INT     21H
  1185. ;------- Terminate and stay resident
  1186.             MOV     DX,RESPARA
  1187.             MOV     AX,3100H
  1188.             INT     21H
  1189.  
  1190. MESSAGE     DB      10,13,'CUTPASTE installed',10,13
  1191.             DB      '   Alt-Fn9 to enable cut ',10,13
  1192.             DB      '      Start/stop marking: Fn1',10,13
  1193.             DB      '      Move 1 space (4 ways): Cursor keys', 10,13
  1194.             DB      '      Move 1 word (2 ways): Ctl-LeftArw, Ctl-RgtArw', 10,13
  1195.             DB      '      Move to screen edge (4 ways): Home, End, PgUp, PgDn', 10,13
  1196.             DB      '      Erase to End of Line: Fn6', 10,13
  1197.             DB      '      Erase Line: Fn7', 10,13
  1198.             DB      '      Add Line: Enter', 10,13
  1199.             DB      '      Move text to right/left: Ins, Del, Bksp', 10,13
  1200.             DB      '   Ctl-Fn9 to paste',10,13,'$'
  1201.  
  1202. COMSEG      ENDS
  1203.             END     START
  1204.  
  1205.